#include "mesh.h"
#include "render_object.h"

#include <fstream>
#include <iostream>
#include <cstring>

void Mesh::LoadFromObj(string sFileName)
{
    struct TFace
    {
        int VertsIndex[3];
        int NormalsIndex[3];
        int TexCoordsIndex[3];
    } *Faces;

    nFaceCount          = 0;
	int nVerticesCount	= 0;
	int nNormalsCount	= 0;
	int nTexCoordsCount = 0;

	Vertex3 *Vertices;
	Vector3 *Normals;
	Vector2 *TexCoords;

    fstream file;
	file.open(sFileName.c_str(), ios::in | ios::out);

	while(file)
	{
			 static char chCommand[10] = "\0";

			 file >> chCommand;

			 if(strcmp("f", chCommand) == 0)
			 {
						 nFaceCount++;
			 }
			 if(strcmp("v", chCommand) == 0)
			 {
						 nVerticesCount++;
			 }
			 if(strcmp("vn", chCommand) == 0)
			 {
						 nNormalsCount++;
			 }
			 if(strcmp("vt", chCommand) == 0)
			 {
						  nTexCoordsCount++;
			 }
	}

	file.close();

	Faces = new TFace[nFaceCount];
	Vertices = new Vertex3[nVerticesCount];
	Normals = new Vector3[nNormalsCount];
	TexCoords = new Vector2[nTexCoordsCount];


	fstream file2;
	file2.exceptions(fstream::failbit);
    file2.open(sFileName.c_str(), ios::in | ios::out);

	int nAddedVerticesCount  = 0;
	int nAddedFacesCount	 = 0;
	int nAddedNormalsCount	 = 0;
	int nAddedTexCoordsCount = 0;

    try{
	while(file2)
	{
			  static char chCommand[10] = "\0";

			  file2 >> chCommand;

			  if(strcmp("f", chCommand) == 0)
			  {
						  file2 >> Faces[nAddedFacesCount].VertsIndex[0];
						  Faces[nAddedFacesCount].VertsIndex[0]--;

						  if(file2.peek() == '/')
						  {
									   file2.ignore(1, ' ');
									   if(file2.peek() == '/')
									   {
													file2.ignore(1, ' ');

													file2 >> Faces[nAddedFacesCount].NormalsIndex[0];
													Faces[nAddedFacesCount].NormalsIndex[0]--;

													file2 >> Faces[nAddedFacesCount].VertsIndex[1];
													Faces[nAddedFacesCount].VertsIndex[1]--;

													file2.ignore(2, ' ');

													file2 >> Faces[nAddedFacesCount].NormalsIndex[1];
													Faces[nAddedFacesCount].NormalsIndex[1]--;

													file2 >> Faces[nAddedFacesCount].VertsIndex[2];
													Faces[nAddedFacesCount].VertsIndex[2]--;

													file2.ignore(2, ' ');

													file2 >> Faces[nAddedFacesCount].NormalsIndex[2];
													Faces[nAddedFacesCount].NormalsIndex[2]--;
									   }
									   else
									   {
										  file2 >> Faces[nAddedFacesCount].TexCoordsIndex[0];
										  Faces[nAddedFacesCount].TexCoordsIndex[0]--;

										  file2.ignore(1, ' ');

										  file2 >> Faces[nAddedFacesCount].NormalsIndex[0];
										  Faces[nAddedFacesCount].NormalsIndex[0]--;

										  file2 >> Faces[nAddedFacesCount].VertsIndex[1];
										  Faces[nAddedFacesCount].VertsIndex[1]--;

										  file2.ignore(1, ' ');

										  file2 >> Faces[nAddedFacesCount].TexCoordsIndex[1];
										  Faces[nAddedFacesCount].TexCoordsIndex[1]--;

										  file2.ignore(1, ' ');

										  file2 >> Faces[nAddedFacesCount].NormalsIndex[1];
										  Faces[nAddedFacesCount].NormalsIndex[1]--;

										  file2 >> Faces[nAddedFacesCount].VertsIndex[2];
										  Faces[nAddedFacesCount].VertsIndex[2]--;

										  file2.ignore(1, ' ');

										  file2 >> Faces[nAddedFacesCount].TexCoordsIndex[2];
										  Faces[nAddedFacesCount].TexCoordsIndex[2]--;

										  file2.ignore(1, ' ');

										  file2 >> Faces[nAddedFacesCount].NormalsIndex[2];
										  Faces[nAddedFacesCount].NormalsIndex[2]--;
									   }
						  }
						  else
						  {
							 file2 >> Faces[nAddedFacesCount].VertsIndex[1];
							 Faces[nAddedFacesCount].VertsIndex[1]--;

							 file2 >> Faces[nAddedFacesCount].VertsIndex[2];
							 Faces[nAddedFacesCount].VertsIndex[2]--;
						  }

						  nAddedFacesCount++;
			  }
			  if(strcmp("v", chCommand) == 0)
			  {
			      file2 >> Vertices[nAddedVerticesCount].x >> Vertices[nAddedVerticesCount].y >> Vertices[nAddedVerticesCount].z;
			      nAddedVerticesCount++;
			  }
			  if(strcmp("vn", chCommand) == 0)
			  {
			      file2 >> Normals[nAddedNormalsCount].x >> Normals[nAddedNormalsCount].y >> Normals[nAddedNormalsCount].z;
			      ++nAddedNormalsCount;
			  }
			  if(strcmp("vt", chCommand) == 0)
			  {
			      file2 >> TexCoords[nAddedTexCoordsCount].x >> TexCoords[nAddedTexCoordsCount].y;
			      nAddedTexCoordsCount++;
			  }
	}
    file2.close();
    }
    catch(ifstream::failure e)
    {
        //Just for stop crashing :P
    }

	fvVertices = new float[nFaceCount*3*3];
	fvNormals = new float[nFaceCount*3*3];
	fvTexCoords = new float[nFaceCount*3*2];

	int nVNCounter = 0;
	int nTexCounter = 0;

	for(int i = 0; i < nFaceCount; ++i)
	{
	    for(int j = 0; j < 3; ++j)
	    {
            fvVertices[nVNCounter] = Vertices[Faces[i].VertsIndex[j]].x;
            fvVertices[nVNCounter+1] = Vertices[Faces[i].VertsIndex[j]].y;
            fvVertices[nVNCounter+2] = Vertices[Faces[i].VertsIndex[j]].z;

            fvNormals[nVNCounter] = Normals[Faces[i].NormalsIndex[j]].x;
            fvNormals[nVNCounter+1] = Normals[Faces[i].NormalsIndex[j]].y;
            fvNormals[nVNCounter+2] = Normals[Faces[i].NormalsIndex[j]].z;

            fvTexCoords[nTexCounter] = TexCoords[Faces[i].TexCoordsIndex[j]].x;
            fvTexCoords[nTexCounter+1] = TexCoords[Faces[i].TexCoordsIndex[j]].y;

            nVNCounter += 3;
            nTexCounter += 2;
	    }
	}
}

void Mesh::PassToRenderObj(RenderObject *pRenderObject)
{
	int v = pRenderObject->NewBuffer(VERTEX_ATTRIB_LOCATION, 3, GL_FLOAT);
	int t = pRenderObject->NewBuffer(TEXCOORD_ATTRIB_LOCATION, 2, GL_FLOAT);
	int n = pRenderObject->NewBuffer(NORMAL_ATTRIB_LOCATION, 3, GL_FLOAT);

	pRenderObject->BufferData(v, nFaceCount*3*3*sizeof(float), fvVertices, GL_STATIC_DRAW, nFaceCount*3);
    pRenderObject->BufferData(t, nFaceCount*3*2*sizeof(float), fvTexCoords, GL_STATIC_DRAW, nFaceCount*3);
	pRenderObject->BufferData(n, nFaceCount*3*3*sizeof(float), fvNormals, GL_STATIC_DRAW, nFaceCount*3);
}

void Mesh::Load(string s)
{
    string ext;

    for(int i = s.length()-1; i >= 0; --i)
    {
        if(s[i] == '.') break;
        ext.insert(ext.begin(), s[i]);
    }

    if(ext == "obj") LoadFromObj(s);
}
